#include "HeapTester.h"

#include <sstream>
#include <fstream>
#include <map>

#include "BinaryHeap.h"
#include "FibonacciHeap.h"

using namespace std;
using namespace algo;

unsigned long long HeapTester::time(std::istream &in, PriorityQueue<int> &Q) {
  unsigned long long start = getTime();
  HeapTester::test(in, Q);
  return getTime() - start;
}

void HeapTester::timeFile(const std::string &file) {
  ifstream fin(file.c_str(), ifstream::in);

  unsigned long long runtime;

  if (fin.good()) {
    BinaryHeap<int> bheap;
    runtime = time(fin, bheap);
    cout << file << ": BinaryHeap took " << runtime << " ms." << endl;

    fin.clear();
    fin.seekg(0);

    FibonacciHeap<int> fheap;
    runtime = time(fin, fheap);
    cout << file << ": FibonacciHeap took " << runtime << " ms." << endl;
  }
}

void HeapTester::test(const std::string &test, PriorityQueue<int> &Q) {
  istringstream in(test);
  HeapTester::test(in, Q);
}

void HeapTester::test(std::istream &in, PriorityQueue<int> &Q) {
  int value;
  std::string cmd;

  map<int, PriorityQueueNode<int> *> nodes;

  size_t line = 0;
  while (in >> cmd) {
    line++;
    if (cmd == "in") {
      in >> value;
      PriorityQueueNode<int> *node = Q.insert(value);
      nodes[value] = node;
    }
    else if (cmd == "mi") {
      in >> value;
      PriorityQueueNode<int> *node = Q.getMin();
      if (node->getKey() != value) {
        errors.addError(line, Q.getName(), "expected min as {0} but was {1} - was doing getMin") 
          << value << node->getKey(); 
      }
    } else if (cmd == "dm") {
      in >> value;
      int min = Q.deleteMin();
      nodes.erase(min);
      if (min != value) {
        errors.addError(line, Q.getName(), "expected min as {0} but was {1} - was doing deleteMin") 
          << value << min; 
      }
    } else if (cmd == "dc") {
      in >> value;
      PriorityQueueNode<int> *node = nodes[value];
      if (!node) {
        errors.addError(line, Q.getName(), 
            "internal test error finding value {0} in map - was doing deleteMin") 
          << value;
        continue;
      }
      nodes.erase(value);
      in >> value;
      Q.decreaseKey(node, value);
      nodes[value] = node;
    }
  }
}

void HeapTester::testFile(const std::string &file) {
  ifstream fin(file.c_str(), ifstream::in);

  if (fin.good()) {
    errors.setFile(file);

    BinaryHeap<int> bheap;
    test(fin, bheap);

    errors.printResult();

    fin.clear();
    fin.seekg(0);

    FibonacciHeap<int> fheap;
    test(fin, fheap);

    errors.printResult();
  }
}

void HeapTester::testFolder(const std::string &dir) {
  vector<string> files = listFolder(dir);

  for (size_t i = 0; i < files.size(); i++) {
    testFile(files[i]);
  }
}

void HeapTester::timeFolder(const std::string &dir) {
  vector<string> files = listFolder(dir);

  for (size_t i = 0; i < files.size(); i++) {
    timeFile(files[i]);
  }
}
